// $URL: http://mingw-lib.googlecode.com/svn/trunk/working/avcodec_to_widget_9/input/t_box_input.cpp $
// $Rev: 312 $
// $Author: akio.miyoshi $
// $Date:: 2010-07-31 03:51:36 +0000#$

#include "t_box_input.h"

class T_Box_InputPrivate
{
public:
    T_Box_InputPrivate()
        : m_fps_dl(5000)
        , m_fps_dl_val(0)
        , m_debugMode(false)
    {
    }
    T_AutoRunFPS m_fps_dl;
    double m_fps_dl_val;
    bool m_debugMode;
};

T_Box_Input::T_Box_Input()
    : m_io_buffer(0)
{
    this->d_ptr = new T_Box_InputPrivate();
    //Q_D(T_Box_Input);
}

T_Box_Input::~T_Box_Input()
{
    delete m_io_buffer;
    delete d_ptr;
}

void T_Box_Input::debugMode(bool b)
{
    Q_D(T_Box_Input);
    d->m_debugMode = b;
}

bool T_Box_Input::debugMode()
{
    Q_D(T_Box_Input);
    return d->m_debugMode;
}

QString T_Box_Input::mediaName()
{
    return m_media_name;
}

void T_Box_Input::mediaName(const QString a_name)
{
    m_media_name = a_name;
}

ByteIOContext *T_Box_Input::getByteIOContext()
{
    if(!this->isValid()) return NULL;
    if(!m_io_buffer)
    {
        m_io_buffer = new quint8[getIOBufferSize()];
    }
    int v_ret = init_put_byte(
            &m_bioc, //ByteIOContext *s
            m_io_buffer, //unsigned char *buffer,
            getIOBufferSize(), //int buffer_size,
            0, //int write_flag,
            this, //void *opaque,
            callback_read_packet, //int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
            NULL, //int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
            callback_seek //int64_t (*seek)(void *opaque, int64_t offset, int whence)
            );
    Q_UNUSED(v_ret);
    //qDebug() << "[T_Box_Input::getByteIOContext()]: v_ret=" << v_ret;
    return &m_bioc;
}

AVInputFormat *T_Box_Input::getAVInputFormat()
{
    const int c_pd_check_size = 4096;
    qDebug() << "T_Box_Input::getAVInputFormat()";
    qint64 v_pos = this->pos();
    this->seek(0);
    uint8_t v_pd_bytes[c_pd_check_size];
    AVProbeData v_probe_data;
    v_probe_data.filename = "";
    v_probe_data.buf_size = this->av_read_packet(v_pd_bytes, sizeof(v_pd_bytes));
    v_probe_data.buf = (unsigned char *)v_pd_bytes;
    this->seek(v_pos);
    AVInputFormat *v_fmt = av_probe_input_format(&v_probe_data, 1);
    if(v_fmt)
    {
        qDebug() << v_fmt->long_name;
        qDebug() << v_fmt->name;
    }
    return v_fmt;
}

AVFormatContext	*T_Box_Input::getAVFormatContext()
{
    if(!this->isValid())
    {
        qDebug() << "(!this->isValid())";
        return NULL;
    }
    AVInputFormat *v_fmt = this->getAVInputFormat();
    if(!v_fmt)
    {
        qDebug() << "(!v_fmt)";
        return NULL;
    }
    AVFormatContext	*v_format_ctx;
    if(::av_open_input_stream(
            &v_format_ctx,
            this->getByteIOContext(),
            "",
            v_fmt,
            NULL))
    {
        return NULL;
    }
    if(::av_find_stream_info(v_format_ctx)<0)
    {
        qDebug() << "Can't find stream info";
        av_close_input_file(v_format_ctx);
        return NULL;
    }
    return v_format_ctx;
}

int T_Box_Input::av_read_packet(uint8_t *buf, int buf_size)
{
    Q_D(T_Box_Input);
    if(d->m_debugMode) qDebug() << "[T_Box_Input::read_packet()]" << buf_size;
#if 0x0
    int v_read_len = this->read_bytes(buf, buf_size);
#else
    QByteArray v_bytes = this->wait_read(buf_size);
    Q_ASSERT(v_bytes.size()<=buf_size);
    memcpy(buf, v_bytes.data(), v_bytes.size());
    int v_read_len = v_bytes.size();
#endif
    d->m_fps_dl.add(v_read_len);
    double v_fps = d->m_fps_dl.value();
    if(v_fps != d->m_fps_dl_val)
    {
        d->m_fps_dl_val = v_fps;
        qDebug() << QString("[%1::read_bytes(1): %2 kB/s]")
                .arg(this->metaObject()->className())
                .arg(v_fps/1024)
                ;
    }
    else if(v_read_len < buf_size)
    {
        double v_fps = d->m_fps_dl.calculate();
        qDebug() << QString("[%1::read_bytes(2): %2 kB/s]")
                .arg(this->metaObject()->className())
                .arg(v_fps/1024)
                ;
    }
    return v_read_len;
}

QString T_Box_Input::p_seek_whence_name(int whence)
{
    switch(whence)
    {
    case SEEK_SET: return "SEEK_SET";
    case SEEK_CUR: return "SEEK_CUR";
    case SEEK_END: return "SEEK_END";
    case AVSEEK_SIZE: return "AVSEEK_SIZE";
    default:
        return QString("[%1]").arg(whence);
    }
}

int64_t T_Box_Input::av_seek(int64_t offset, int whence)
{
    Q_D(T_Box_Input);
    QString v_whence_name = p_seek_whence_name(whence);
    if(d->m_debugMode)
    {
        if(whence == SEEK_CUR)
        {
            qDebug() << "[seek()]" <<v_whence_name << offset << pos();
        }
        else
        {
            qDebug() << "[seek()]" <<v_whence_name << offset;
        }
    }
    switch(whence)
    {
    case SEEK_SET:
        break;
    case SEEK_CUR:
        {
            qint64 v_pos = this->pos();
            if(v_pos < 0) return -1;
            offset += v_pos;
        }
        offset += this->pos();
        break;
    case SEEK_END:
        {
            qint64 v_size = this->size();
            if(v_size < 0) return -1;
            offset += v_size;
        }
        break;
    case AVSEEK_SIZE:
        // return the filesize without seeking anywhere
        return this->size();
    default:
        return -1;
    }
    if(this->seek(offset))
    {
        return this->pos();
    }
    return -1;
}

int T_Box_Input::callback_read_packet(void *opaque, uint8_t *buf, int buf_size)
{
    T_Box_Input *self = (T_Box_Input *)opaque;
    return self->av_read_packet(buf, buf_size);
}

int64_t T_Box_Input::callback_seek(void *opaque, int64_t offset, int whence)
{
    T_Box_Input *self = (T_Box_Input *)opaque;
    return self->av_seek(offset, whence);
}
